﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Runtime.InteropServices;
using System.Diagnostics;
using AOC19NET.Classes;
using AOC19NET.Controls;
using AOCRivaTunerReader;

namespace AOC19NET {
	/// <summary>
	/// Tiva Tuner Hardware Monitor info class.
	/// </summary>
	class RivaTunerPage {
		/*-------------------------------------------------------------------------*/
		/* DEFINITIONS															   */
		/*-------------------------------------------------------------------------*/
		#region DEFINITIONS
		#endregion

		/*-------------------------------------------------------------------------*/
		/* MEMBERS																   */
		/*-------------------------------------------------------------------------*/
		#region MEMBERS
			/// <summary>
			/// Custom control containing all the elements on the LCD screen
			/// </summary>
			public RivaTunerControl m_MainControl;

			/// <summary>
			/// The CPU amount of bars can vary depending on each processor.
			/// We'll create a vertical bar for each core and keep a reference to them
			/// in this list in order to update them.
			/// </summary>
			private List<VerticalBarGraph> m_vCPUBars;

			/// <summary>
			/// Keep the base color to apply it at each update.
			/// </summary>
			private Color m_BaseColor;

			/// <summary>
			/// This class will allow us to read the Riva Tuner Shared Memory.
			/// </summary>
			private CRivaTunerReader m_RTReader;

			/// <summary>
			/// All the entries read from m_RTReader. This will contain each entry identifier (e.g. "CPU0"),
			/// which will allow us to access its correspondant struct.
			/// </summary>
			private List<String> m_vRTEntries;

			/// <summary>
			/// This class will allow us to read some system metrics such as CPU and RAM usage.
			/// </summary>
			private CWindowsPerformanceUtils m_PerfUtils;

			/// <summary>
			/// To control the Render frequency: We'll update once per second.
			/// </summary>
			private DateTime m_LastUpdate;
		#endregion

		/*-------------------------------------------------------------------------*/
		/* METHODS																   */
		/*-------------------------------------------------------------------------*/
		#region METHODS
			/// <summary>
			/// Constructor of the class: sets all the members to its default values.
			/// </summary>
			public RivaTunerPage() {
				// Initialize custom controls
				m_MainControl = new RivaTunerControl();
				m_vCPUBars = new List<VerticalBarGraph>();
				m_BaseColor = Colors.LightGray;
				m_RTReader = new CRivaTunerReader();
				m_vRTEntries = new List<String>();
				m_PerfUtils = new CWindowsPerformanceUtils();
			}

			/// <summary>
			/// Actions to do once at the beginning.
			/// </summary>
			public void Init() {
				m_LastUpdate = DateTime.Now;

				InitializeCPUBars();
				LoadSettings();
			}

			/// <summary>
			/// Actions to do during the update process.
			/// To be called periodically.
			/// </summary>
			public void Run() {
				// Update the Riva Tuner reader
				m_RTReader.Update();

				// Get the new entries list
				m_vRTEntries.Clear();
				m_vRTEntries.AddRange(m_RTReader.GetEntriesList());
			}

			/// <summary>
			/// Actions to do during the render process, typically update the graphics.
			/// To be called periodically.
			/// </summary>
			public void Render() {
				// Update just once per second
				double dMillis = (DateTime.Now - m_LastUpdate).TotalMilliseconds;
				if(dMillis >= 1000) {
					m_LastUpdate = DateTime.Now;

					// Bars
					for(int i = 0; i < m_vCPUBars.Count; i++) {
						float fCPUUsage = m_PerfUtils.GetCPUUsage(i) / 100.0f;
						m_vCPUBars[i].SetValue(fCPUUsage);
						m_vCPUBars[i].m_BarForeground.Fill = new SolidColorBrush(ColorHelper.Interpolate(Colors.GreenYellow, Colors.Red, fCPUUsage));
					}
					m_MainControl.m_RAMGraph.SetValue(1.0f - m_PerfUtils.GetAvailableMemoryPercentage() / 100.0f);

					// Rivatuner Histograms
					CRivaTunerPageSettings CurrentSettings = Properties.Settings.Default.RTPageSettings;
					m_MainControl.m_RTGraph0Label.Content = CurrentSettings.m_vGraphLabels[0];	// The first field is special.
					UpdateHistogram(m_MainControl.m_RTGraph0, CurrentSettings.m_vRTKeys[0], m_MainControl.m_RTGraph0Value, "{0:0.}");
					UpdateHistogram(m_MainControl.m_RTGraph1, CurrentSettings.m_vRTKeys[1], m_MainControl.m_RTGraph1Label, CurrentSettings.m_vGraphLabels[1] + ": {0:0.} {1}");
					UpdateHistogram(m_MainControl.m_RTGraph2, CurrentSettings.m_vRTKeys[2], m_MainControl.m_RTGraph2Label, CurrentSettings.m_vGraphLabels[2] + ": {0:0.} {1}");
					UpdateHistogram(m_MainControl.m_RTGRaph3, CurrentSettings.m_vRTKeys[3], m_MainControl.m_RTGraph3Label, CurrentSettings.m_vGraphLabels[3] + ": {0:0.} {1}");
					UpdateHistogram(m_MainControl.m_RTGraph4, CurrentSettings.m_vRTKeys[4], m_MainControl.m_RTGraph4Label, CurrentSettings.m_vGraphLabels[4] + ": {0:0.} {1}");

					// Net Histograms
					float fInKB = m_PerfUtils.GetNetInTraffic();
					m_MainControl.m_RTGraphNetIn.NewValue(fInKB);
					m_MainControl.m_RTGraphNetInLabel.Content = String.Format("DL: {0:0.} KB/s", fInKB);

					float fOutKB = m_PerfUtils.GetNetOutTraffic();
					m_MainControl.m_RTGraphNetOut.NewValue(fOutKB);
					m_MainControl.m_RTGraphNetOutLabel.Content = String.Format("UL: {0:0.} KB/s", fOutKB);

					// Date and Time
					m_MainControl.m_HourDateLabel.Content = DateTime.Now.ToString("HH:mm:ss    dd/MM/yy");
				}
			}

			/// <summary>
			/// Changes the current color scheme.
			/// </summary>
			/// <param name="_NewBaseColor">The new base color. Each element color will be a tint of this one.</param>
			public void ChangeColorScheme(Color _NewBaseColor) {
				// Store base color
				m_BaseColor = _NewBaseColor;

				// Change background color
				LinearGradientBrush BckgBrush = (LinearGradientBrush)m_MainControl.Background;
				BckgBrush.GradientStops[BckgBrush.GradientStops.Count - 1].Color = _NewBaseColor;
				m_MainControl.Background = BckgBrush;
			}

			/// <summary>
			/// Load settings from the default profile.
			/// </summary>
			public void LoadSettings() {
				CRivaTunerPageSettings CurrentSettings = Properties.Settings.Default.RTPageSettings;
				m_MainControl.m_RTGraph0Label.Content = CurrentSettings.m_vGraphLabels[0];
				m_MainControl.m_RTGraph1Label.Content = CurrentSettings.m_vGraphLabels[1];
				m_MainControl.m_RTGraph2Label.Content = CurrentSettings.m_vGraphLabels[2];
				m_MainControl.m_RTGraph3Label.Content = CurrentSettings.m_vGraphLabels[3];
				m_MainControl.m_RTGraph4Label.Content = CurrentSettings.m_vGraphLabels[4];

				// Max values
				InitializeHistograms();
			}
		#endregion

		#region PRIVATE METHODS
			/// <summary>
			/// Intialize each histogram with its maximum value and other params.
			/// </summary>
			private void InitializeHistograms() {
				CRivaTunerPageSettings CurrentSettings = Properties.Settings.Default.RTPageSettings;
				m_MainControl.m_RTGraph0.SetMaxValue(CurrentSettings.m_vGraphMaxValues[0]);
				m_MainControl.m_RTGraph1.SetMaxValue(CurrentSettings.m_vGraphMaxValues[1]);
				m_MainControl.m_RTGraph2.SetMaxValue(CurrentSettings.m_vGraphMaxValues[2]);
				m_MainControl.m_RTGRaph3.SetMaxValue(CurrentSettings.m_vGraphMaxValues[3]);
				m_MainControl.m_RTGraph4.SetMaxValue(CurrentSettings.m_vGraphMaxValues[4]);
				m_MainControl.m_RTGraphNetIn.SetMaxValue(CurrentSettings.m_vGraphMaxValues[5]);
				m_MainControl.m_RTGraphNetOut.SetMaxValue(CurrentSettings.m_vGraphMaxValues[5]);
			}

			/// <summary>
			/// Initializes all the CPU bars, depending on the amount of cores our processor has.
			/// </summary>
			private void InitializeCPUBars() {
				// Initialize the CPU bars, depending on the cores the system has
				m_vCPUBars.Add(m_MainControl.m_CPU0Graph);	// Add the first bar
				int iNumCPUs = Environment.ProcessorCount;
				for(int i = 1; i < iNumCPUs; i++) {
					// Create a new bar and apply the default style
					VerticalBarGraph NewCPU = new VerticalBarGraph();
					NewCPU.Margin = new Thickness(0, 0, -10, 0);
					NewCPU.Height = double.NaN;	// Auto
					NewCPU.Label = String.Format("CPU{0}", i);
					NewCPU.LabelColor = Brushes.DarkGray;
					NewCPU.BarForegroundColor = Brushes.LightBlue;
					NewCPU.BarBackgroundColor = new SolidColorBrush(Color.FromArgb(0xFF, 0x44, 0x44, 0x44));

					// Add the new bar to the main control - at the end but before the RAM bar
					m_MainControl.m_CPUStackPanel.Children.Insert(i, NewCPU)/*Add(NewCPU)*/;

					// Add the new bar to the list
					m_vCPUBars.Add(NewCPU);
				}

				// The width of the bars will depend on the number of cores
				double dNewWidth = double.NaN;	// Auto
				double dNewRightMargin = -10;
				switch(iNumCPUs) {
					case 0:
					case 1:
					case 2:
						dNewWidth = 50;
						dNewRightMargin = -15;
						break;

					default:
						dNewWidth = 40;
						break;
				}

				// Set the width of each bar (and the RAM bar)
				for(int i = 0; i < iNumCPUs; i++) {
					m_vCPUBars[i].Width = dNewWidth;
					Thickness NewMargin = m_vCPUBars[i].Margin;
					NewMargin.Right = dNewRightMargin;
					m_vCPUBars[i].Margin = NewMargin;
				}
				m_MainControl.m_RAMGraph.Width = dNewWidth;
			}

			/// <summary>
			/// Updates the specified Histogram object using the values obtained from the entry ID _sKeyID.
			/// </summary>
			/// <param name="_Histogram">The Histogram object to be updated.</param>
			/// <param name="_sKeyID">RTHM entry ID.</param>
			/// <param name="_Label">Label linked to the Histogram object.</param>
			/// <param name="_sLabelFormat">The formatted label content. The first argument {0} will be the value and the second {1} the dimension.</param>
			private void UpdateHistogram(Histogram _Histogram, String _sKeyID, Label _Label, String _sLabelFormat) {
				// Check if the given ID is valid.
				if(!m_vRTEntries.Contains(_sKeyID)) {
					_Histogram.NewValue(0);
					_Label.Content = String.Format(_sLabelFormat, 0, "");
				} else {
					CRivaTunerFieldData RTData = m_RTReader.GetEntry(_sKeyID);
					_Histogram.NewValue(RTData.m_fValue);
					_Label.Content = String.Format(_sLabelFormat, RTData.m_fValue, RTData.m_sDimension);
				}
			}
		#endregion
	}

	/// <summary>
	/// This will allow us to easily Save and Load the RT page settings from the global settings.
	/// </summary>
	[Serializable]
	public class CRivaTunerPageSettings {
		#region MEMBERS
		/// <summary>
		/// The label of each RT histogram
		/// </summary>
		public string[] m_vGraphLabels = {"FPS", "GPU0", "GPU1", "GPU0", "GPU1", ""};

		/// <summary>
		/// The maximum value of each RT histogram
		/// </summary>
		public float[] m_vGraphMaxValues = {100, 100, 100, 100, 100, 600};

		/// <summary>
		/// The RT ID of the assigned monitor of each RT histogram.
		/// </summary>
		public string[] m_vRTKeys = {"Framerate", "#0:Fan duty cycle", "#1:Fan duty cycle", "#0:Core temperature", "#1:Core temperature"};
		#endregion
	}
}
